package com.kedacom.ctsp.authz.oauth2.client.service;

import com.alibaba.fastjson.JSON;
import com.kedacom.ctsp.authz.oauth2.client.OAuth2ClientProperties;
import com.kedacom.ctsp.authz.oauth2.core.*;
import com.kedacom.ctsp.authz.oauth2.core.vo.AccessToken;
import com.kedacom.ctsp.authz.oauth2.core.vo.ClientQueryParam;
import com.kedacom.ctsp.authz.oauth2.core.vo.UserQueryParam;
import com.kedacom.ctsp.lang.EnumUtil;
import com.kedacom.ctsp.web.controller.message.ResponseMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.UUID;

import static com.kedacom.ctsp.authz.oauth2.core.ErrorType.ACCESS_DENIED;

/**
 * Created by lyc28724 on 2016/6/15.
 */
@Slf4j
@Service
@EnableConfigurationProperties(OAuth2ClientProperties.class)
public class OAuth2ServiceImpl implements OAuth2Service {

    @Autowired
    private OAuth2ClientProperties oAuth2Properties;

    @Autowired
    private OAuth2AccessTokenService accessTokenService;
    @Autowired
    private OAuth2ResourceService resourceService;

    @Override
    public String authorize(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 如果cookie中没有token信息，通过state向SSO站点申请授权码
        String state = UUID.randomUUID().toString().replaceAll("-", "");
        // 用于防止跨站请求伪造（CSRF）攻击
        Cookie cookie = new Cookie("state", state);
        cookie.setPath(oAuth2Properties.getCookiePath());
        response.addCookie(cookie);
        StringBuffer fullUri = new StringBuffer();
        String redirectUri = URLEncoder.encode(oAuth2Properties.getClientUri() + "/oauth2/callback", "utf-8");
        String returnUrl = request.getAttribute("returnUri") == null ? "" : request.getAttribute("returnUri").toString();

        if (null != returnUrl) {
            returnUrl = URLEncoder.encode(URLEncoder.encode(returnUrl, "utf-8"), "utf-8");
        }

        fullUri.append(oAuth2Properties.getServerUri())
                .append("/authorize")
                .append("?response_type=").append(ResponseType.CODE)
                .append("&scope=").append(oAuth2Properties.getScope())
                .append("&client_id=").append(oAuth2Properties.getClientId())
                .append("&redirect_uri=").append(redirectUri)
                .append("&state=").append(state)
                .append("&return_uri=").append(returnUrl);

        log.info(String.format("========步骤是:%d_方法是:%s_功能是:%s,参数是:%s",2, "OAuth2ServiceImpl.authorize", "向SSO站点申请访问令牌", fullUri));
        return fullUri.toString();
    }

    @Override
    public Boolean checkState(HttpServletRequest request, HttpServletResponse response, String state) {
        if (null != request.getCookies()) {
            for (Cookie cookie : request.getCookies()) {
                if ("state".equals(cookie.getName())) {
                    if (state.equals(cookie.getValue())) {
                        cookie.setMaxAge(0);
                        cookie.setPath(oAuth2Properties.getCookiePath());
                        response.addCookie(cookie);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    @Override
    public OAuth2Authentication getAuthenticationByAccessToken(AccessToken accessToken) {
        UserQueryParam param = new UserQueryParam();
        param.setAccessToken(accessToken.getAccessToken());
        param.setClientId(oAuth2Properties.getClientId());
        param.setClientSecret(oAuth2Properties.getClientSecret());
        ResponseMessage<OAuth2Authentication> response = resourceService.getUserByAccessToken(param);
        if (response.is2xxSuccessful()) {
            return response.getResult(OAuth2Authentication.class);
        }
        return null;
    }

    @Override
    public AccessToken applyForAuthorizationCode(String code) {
        AccessTokenRequest request = AccessTokenRequest.AuthorizationCodeTokenRequestBuilder.newBuilder()
                .clientId(oAuth2Properties.getClientId())
                .clientSecret(oAuth2Properties.getClientSecret())
                .redirectUri(oAuth2Properties.getClientUri() + "/oauth2/callback")
                .code(code)
                .build();

        AccessToken accessToken = applyForAccessToken(request);
        return accessToken;
    }


    @Override
    public AccessToken applyForPassword(String username, String password) {

        AccessTokenRequest request = AccessTokenRequest.PasswordRequestBuilder.newBuilder()
                .username(username)
                .password(password)
                .clientId(oAuth2Properties.getClientId())
                .clientSecret(oAuth2Properties.getClientSecret())
                .build();

        AccessToken accessToken = applyForAccessToken(request);
        return accessToken;
    }

    /**
     * 客户端模式
     *
     * @return
     */
    @Override
    public AccessToken applyForClientCredential() {
        AccessTokenRequest request = AccessTokenRequest.ClientCredentialRequestBuilder.newBuilder()
                .clientId(oAuth2Properties.getClientId())
                .clientSecret(oAuth2Properties.getClientSecret())
                .build();
        AccessToken accessToken = applyForAccessToken(request);
        return accessToken;
    }

    /**
     * 刷新accessToken
     *
     * @param refreshToken
     * @return
     */
    @Override
    public AccessToken applyForRefreshToken(String refreshToken) {
        AccessTokenRequest request = AccessTokenRequest.RefreshTokenRequestBuilder.newBuilder()
                .clientId(oAuth2Properties.getClientId())
                .clientSecret(oAuth2Properties.getClientSecret())
                .refreshToken(refreshToken)
                .build();

        AccessToken accessToken = applyForAccessToken(request);
        return accessToken;
    }

    /**
     * 申请accessoken
     *
     * @param request
     * @return
     */
    @Override
    public AccessToken applyForAccessToken(AccessTokenRequest request) {
        ResponseMessage<AccessToken> response;
        try {
            log.info("获取===========================applyForAccessToken" + JSON.toJSONString(request));
            response = accessTokenService.requestToken(request.getGrantType(), request.getClientId(), request.getClientSecret(), request.getRedirectUri(), request.getCode(), request.getUsername(), request.getPassword(),request.getRefreshToken());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new OAuth2Exception(ACCESS_DENIED, e.getMessage());
        }
        if (response.is2xxSuccessful()) {
            return response.getResult(AccessToken.class);
        }
        throw new OAuth2Exception(response.getStatus(),response.getCode(), response.getMessage());
    }

}
